Variables are places in computer memory for storing changing information. You use variables to keep records for your program, such as whether the user has pushed a button or not, how many times she’s pushed the button, how many times the microcontroller has flashed a light, how much time has passed since the last button push, and so forth.
Think of computer memory as a bunch of coffee cups that you can put a label on the outside and store things on the inside. Variables allow you to put your own names on the outside of the coffee cup and put things you want to remember inside of it. You can then use the stuff inside the cups by referring to them by name in your if statements and loops can then find the coffee cups by name and take different actions based on what’s there. The real power comes not from the fact that you can place things in variables, but that you can replace them or “vary” them easily.
Before you use a variable in most languages (BASIC, C, Wiring, etc.) you need to give it a name, or declare your variable. Normally, you do this at the very beginning of your program or at the beginning of a routine. Use a name that describes what you’re using the variable to remember, because it will make your code much more readable. Some people like to add “Var” to the ends of variable names, to make them easy to identify when reading the code. You can use any name you want for your variable as long as it does not start with a number, has no spaces, and isn’t a keyword. When you try to run your program, the compiler will let you know if your variable name isn’t allowed.
To store a value in a variable you put the name of the variable on the left side of an equation and the value you want to remember on the right, like so:
fooVar = 12 sensorVar = 250 switchVar = 125
Every variable has a data type. The data type of a variable determines how much memory the microcontroller needs for the variable, and how it will use the data stored in the variable. You declare both the name and the data type of the variable before you use it, like so:
Wiring/Arduino:
char fooVar; int barVar; long timeVar;
PicBasic Pro:
byteVar var word switchVar var bit bigVar var word
BX-Basic:
dim byteVar as byte dim bigVar as integer dim fractionVar as single
Here are the data types for the PIC and BX-24, broken down by the amount of memory each needs:
Wiring/Arduino:
Data Type | Memory needed | Description | Range | Example |
char | 8 bits | An ASCII character value | 0 – 255 | thisVar = ‘A’ |
byte | 8 bits | n positive integer number | -128 to 127 | numVar = 45 |
int | 16 bits | also an integer number. Integer variables can store a greater range of values than Byte variables, however. | -32 768 to 32 767 | potVar = 4857 |
long | 32 bits | also an integer number, but a greater range than Byte or Integer variables. Can also be positive or negative. | -2,147,483,648 to 2,147,483,647 | starsVar = 65535 |
PicBasic Pro:
Data Type | Memory needed | Description | Range | Example |
Bit | 1 | a 1 or 0 value | 1 or 0 | thisVar = 1 |
Byte | 8 bits | a positive integer number | 0 to 255 | numVar = 45 |
Word | 16 bits | also an integer number. Integer variables can store a greater range of values than Byte variables, however. | 0 to 65,535 | potVar = 4857 |
BX-24:
Data Type | Memory needed | Description | Range | Example |
Boolean | 8 bits | a true or false value | True or False | thisVar = true |
Byte | 8 bits | a positive integer number | 0 to 255 | numVar = 45 |
Integer | 16 bits | also an integer number. Integer variables can store a greater range of values than Byte variables, however, and part of that range can be negative. | -32 768 to 32 767 | potVar = 4857 |
Long | 32 bits | also an integer number, but a greater range than Byte or Integer variables. Can also be positive or negative. | -2,147,483,648 to 2,147,483,647 | starsVar = 65535 |
Single | 32 bits | a floating-point number (i.e. a number with a fractional part, like 2.5, 3.14159, etc.). Note that all floating point numbers must have a decimal, even if they have no fractional part. E.g. 4.0 instead of just 4. | -3.402,823 E+38 .. 3.402,823 E+38 | angleVar = 4.5 |
String | Varies | an array of bytes representing alphanumeric characters. | 0 to 64 bytes (characters) | myStringVar = “Hello” |
To understand how variables are stored in memory, it’s useful to think about what memory is. a computer’s memory is basically a matrix of switches, laid out in a regular grid, not unlike the switches you see on the back of a lot of electronic gear:
Each switch represents the smallest unit of memory, a bit (usually, we think in terms of bytes when talking about memory. a byte is simply eight bits). If the switch is on, the bit’s value is 1. If it’s off, the value is 0. Each bit has an address in the grid. We can envision a grid that represents that memory like this:
bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 |
bit8 | bit9 | bit10 | bit11 | bit12 | bit13 | bit14 | bit15 |
– | – | – | – | – | – | – | – |
– | – | – | – | – | – | – | – |
– | – | – | – | – | – | – | – |
– | – | – | – | – | – | – | – |
– | – | – | – | – | – | – | – |
– | – | – | – | – | – | – | – |
This grid is arranged in rows of 8 bits; each row represents a byte of memory in this illustration.
When you declare a variable, the microcontroller picks the next available address and sets aside as many bits are needed for the data type you declare. If you a byte, for example, it sets aside 8 bits. An integer gets 16 bits. A string gets one byte (eight bits) for every character of the string, and a byte to end the string.
If you made the following variable declarations at the top of your program:
byte thisVar; int biggerVar; byte anotherVar; long reallyBigVar;
The microcontroller might assign memory space for those variables something like this (the bits set aside for each variable are color-coded):
myVar | – | – | – | – | – | – | myVar |
integerVar | – | – | – | – | – | – | – |
– | – | – | – | – | – | – | integerVar |
anotherVar | – | – | – | – | – | – | anotherVar |
reallyBigVar | – | – | – | – | – | – | – |
– | – | – | – | – | – | – | – |
– | – | – | – | – | – | – | – |
– | – | – | – | – | – | – | reallyBigVar |
in PicBasic Pro for the PIC microcontroller,you only have bit-, byte-, and word-sized variables, so you your grid might look like this:
switch1Var var bit switch2Var var bit switch3Var var bit thisVar var byte thatVar var word
switch1Var | switch1Var | switch2Var | switch3Var | – | – | – | – |
thisVar | – | – | – | – | – | – | thisVar |
thatVar | – | – | – | – | – | – | – |
– | – | – | – | – | – | – | thatVar |
The space after the bit variables isn’t filled by the next byte variable. The byte variable starts at the next byte in memory.
When you refer to the variable, the microcontroller checks to see what’s in those bits, and gives them to you.
So if a bit can be only 0 or 1, how do we get values greater than 1?
When you count normally, you count in groups of ten. This is because you have ten fingers. So to represent two groups of ten, you write “20”, meaning “2 tens and 0 ones”. This counting system is called base ten, or decimal notation. Each digit place in base ten represents a power of ten: 100 is 102, 1000 is 103, etc.
Now, imagine you had only two fingers. You might count in groups of two. This is called base two, or binary notation. So two, for which you write “2” in base ten, would be “10” in base two, meaning one group of two and 0 ones. Each digit place in base two represents a power of two: 100 is 22, or 4 in base ten, 1000 is 23, or 8 in base ten, and so forth.
Any number you represent in decimal notation can be converted into binary notation by simply regrouping it in groups of two. Once you’ve got the number in binary form, you can store it in computer memory, letting each binary digit fill a bit o memory. So if the variable myVar from above were equal to 238 (in decimal notation), it would be 11101110 in binary notation. The bits in memory used to store myVar would look like this:
1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 |
There are three notation systems used most commonly in programming languages to represent numbers: binary (base two), decimal (base ten), and hexadecimal (base sixteen). In hexadecimal notation, the letters A through F represent the decimal numbers 10 through 15. Furthermore, there is a system of notation called ASCII, which stands for American Standard Code for Information Interchange, which represents most alphanumeric characters from the romanized alphabet as number values. More on ASCII can be found in the pages on serial communication. For more, see this online table representing the decimal numbers 0 to 255 in decimal, binary, hexadecimal, and ASCII. While you can work mostly in decimal notation, there are times when it’s more convenient to represent numbers in ms other than base 10.
Here’s a chart showing a few number values in the different bases, and the different notation forms for a few programming languages:
Ten:
Language | Decimal | Hexadecimal | Binary |
PicBasic Pro, pBasic | 10 |
$A |
%1010 |
BX-Basic | 10 |
&HA |
bx1010 |
Wiring/Arduino | 10 |
0x0A |
B1010 |
One hundred sixty-three:
Language | Decimal | Hexadecimal | Binary |
PicBasic Pro, pBasic | 163 |
$A3 |
%10100011 |
BX-Basic | 163 |
&HA3 |
bx1010_0011 |
Wiring/Arduino | 163 |
0x0A3 |
B10100011 |
Variable scope
Variables are local to a particular subroutine if they are declared in that subroutine. Local variables can’t be used by subroutines outside the one that declares them, and the memory space allotted to them is released and the value lost when the subroutine ends. Variables are global when they are declared at the beginning of a piece of code, outside all subroutines. Global variables are accessible to all subroutines in a the code, and their value is maintained for the duration of the program. Usually you use global variables for values that will need to be kept in memory for future use by other subroutines, and local variables as a “scratch pad” to store values while calculating within a subroutine.
PicBasic Pro and pBasic variables are all global in scope.
Doing Arithmetic With Variables
There are certain symbols you’ll need to do math in a program. They’re pretty much the same symbols as you use in other programming languages:
Operation | BX-24 | PBPro | Wiring/Arduino |
Addition | + | + | + |
Subtraction | – | – | – |
Multiplication | * | * | * |
Divide (float) | / | none | none |
Divide (integer) | \ | / | / |
Modulus | Mod | // | % |
Absolute value | Abs | Abs | abs |
sensorVar var byte counterVar var byte bigVar var word bigVar = 500 sensorVar = 200 counterVar = 70
sensorVar = sensorVar + counterVar
The result is 270, which is more than can fit in a byte. What happens in this case is that the variable rolls over. If we put 256 in the variable, it reads as 0, 257, reads as 1, and so forth. Counting this way, 270 would read as 14.
sensorVar = bigVar - 10
The result is 490. This doesn’t fit in a byte variable, so the result will roll over. The result would read as 235.
BX-Basic Data Type Conversions
In BX-Basic, when you’re adding, subtracting, multiplying, or dividing values stored in variables, you must make sure that the variables you’re operating on are of the same type. For example:
dim someVar as byte dim anotherVar as byte dim smallVar as byte dim bigVar as integer dim yetAnotherVar as integer Sub main() call delay(0.5) ' start program with a half-second delay someVar = anotherVar + smallVar ' allowed, because all three ' variables are data type byte somevar = anotherVar * 3 ' allowed, because the BX-24 ' interprets 3 as a byte someVar = anotherVar + bigVar ' not allowed, because ' bigVar is an integer, while ' anotherVar and someVar are bytes someVar = anotherVar - 568 ' not allowed, because 568 ' is larger than a byte yetAnotherVar = bigvar + 568 ' allowed, because 568 will fit ' in an integer variable yetAnotherVar = bigvar * someVar ' not allowed, because someVar ' is a byte and the other two ' variables are integers end sub
There are conversion functions to convert data types. For example:
BX-Basic:
bigVar = cInt(someVar) ' sets bigVar = an integer ' of equal value to someVar's value debug.print cStr(65) ' converts value to ASCII bytes, ' the character "6" and the character "5"
See the BX-24 system library for all the conversion functions.
Constants
In addition to variables, every programming language also includes constants, which are simply variables that don’t change. They’re a useful way to label and change numbers that get used repeatedly within your program. For example, imagine you’re writing a program that runs a servo motor. Servo motors have a minimum and maximum pulse width that doesn’t change, although each servo’s minimum and maximum might be somewhat different. Rather than change every occurrence of the minimum and maximum numbers in the program, we make them constants, so we only have to change the number in one place.
In PicBasic Pro, constants are declared at the beginning of your program, like so:
MinPulse con 100
Then you can refer to them in the program just like you do variables, like so:
PulseWidth = minPulse + angleVar
In BX Basic, we also have to declare the type of the constant, like so:
Const minPulse as single = 0.001
You don’t have to use constants in your programs, but they’re handy to know about, and you will encounter them in other people’s programs.
In Wiring/Arduino, there are two ways you can declare constants. You can use the const keyword, like so:
const int LEDpin = 3; const int sensorMax = 253;
Or you can use a function called define:
#define LEDpin 3 #define sensorMax 253
Note that defines are always preceded by a #, and are don’t have a semicolon at the end of the line. Defines always come at the beginning of the program. They actually work a bit like aliases. What happens is that you define a number as a name, and before compiling, the compiler checks for all occurrences of that name in the program and replaces it with the number. This way, defines don’t take up any memory, but you get all the convenience of a named constant. There are several defines in the libraries of the Arduino core libraries, so it’s preferable to use const instead of #define for constants.